﻿using housingsafety.server.Helpers;
using housingsafety.server.Svcs;
using FFMpegCore;
using FFMpegCore.Enums;
using FFMpegCore.Pipes;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace housingsafety.server.SvcImpls
{
    public class DiskImageCacheProvider : IImageCacheProvider
    {
        private readonly ILogger<DiskImageCacheProvider> _logger;

        public DiskImageCacheProvider(ILogger<DiskImageCacheProvider> logger)
        {
            this._logger = logger;
        }
        public string ProviderName => "Disk";

        private string EnsureCachesFolder()
        {
            string basePath1 = AppContext.BaseDirectory;
            string baseFolder = Path.Combine(basePath1, "caches");
            Directory.CreateDirectory(baseFolder);
            return baseFolder;
        }

        private string GetStoreFilePath(string fileId)
        {
            string baseFolder = EnsureCachesFolder();
            if (fileId.Length < 3)
            {
                string storeFilePath = Path.Combine(baseFolder, $"{fileId}.png");
                return storeFilePath;
            }
            else
            {
                //前2位作为子目录，后XX位作为文件名保存在子目录下
                string subFolder = Path.Combine(baseFolder, fileId.Substring(0, 2));
                Directory.CreateDirectory(subFolder);
                string storeFilePath = Path.Combine(subFolder, $"{fileId.Substring(2)}.png");
                return storeFilePath;
            }
        }

        public async Task Cache(string fileId, string contentType, Stream objectStream)
        {
            if (string.IsNullOrEmpty(fileId)
                || null == objectStream
                || objectStream.Position != 0
                || objectStream.Length < 1
                || !objectStream.CanRead)
            {
                return;
            }

            string storeFilePath = GetStoreFilePath(fileId);
            if (DotAttachContentTypes.IsVideo(contentType))
            {
                var tempFile = Path.GetTempFileName();
                if (MakeVideoThumbnail(objectStream, tempFile))
                {
                    var fs = File.Open(tempFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                    MakeImageThumbnail(fs, storeFilePath);
                    fs.Dispose();
                    fs.Close();
                    File.Delete(tempFile);
                }
            }
            else if (DotAttachContentTypes.IsImage(contentType))
            {
                MakeImageThumbnail(objectStream, storeFilePath);
            }
        }

        private void MakeImageThumbnail(Stream objectStream, string outImg)
        {
            UtilExtenions.Thumbnail(objectStream, outImg);
        }

        private bool MakeVideoThumbnail(Stream fs, string outImg)
        {
            try
            {
                File.Delete(outImg);

                var pipeSource = new StreamPipeSource(fs);
                var arguments = FFMpegArguments.FromPipeInput(pipeSource);

                arguments.OutputToFile(outImg, true, options => options
                    .ForceFormat("rawvideo")
                    .WithVideoCodec(VideoCodec.Png)
                    .WithFrameOutputCount(1))
                .ProcessAsynchronously()
                .ConfigureAwait(false).GetAwaiter().GetResult();

                return new FileInfo(outImg).Exists;
            }
            catch (IOException ex)
            {
                _logger.LogError(ex.InnerMessage());
                return false;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.InnerMessage());
                return new FileInfo(outImg).Exists;
            }
        }

        public bool ExistFile(string fileId)
        {
            if (string.IsNullOrEmpty(fileId))
            {
                return false;
            }

            string storeFilePath = GetStoreFilePath(fileId);
            return File.Exists(storeFilePath);
        }

        public async Task<Stream> GetCacheStreamAsync(string fileId)
        {
            if (string.IsNullOrEmpty(fileId))
            {
                return new MemoryStream();
            }

            string storeFilePath = GetStoreFilePath(fileId);
            if (!File.Exists(storeFilePath))
            {
                return new MemoryStream();
            }

            return await Task.FromResult(File.Open(storeFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));

        }

        public async Task<Stream> BuildCacheStreamAsync(string fileId, string contentType, Task<Stream> oriImg)
        {
            if (!ExistFile(fileId))
            {
                await Cache(fileId, contentType, oriImg.Result);
            }

            if (!ExistFile(fileId))
            {
                if (DotAttachContentTypes.IsImage(contentType))
                {
                    return oriImg.Result;
                }
                else
                {
                    return new MemoryStream();
                }
            }

            return await GetCacheStreamAsync(fileId);
        }
    }
}
